#include <iostream>
// #include "Test.h"
#include "Hazel.h"
#include "imgui.h"

#include "glm/gtc/matrix_transform.hpp"
// #include "opencv2/opencv.hpp"
#include "glad/glad.h"
#include "glm/gtc/type_ptr.hpp"
#include "platform/OpenGL/OpenGLShader.h"

#include "Sandbox2D.h"

//----------EntryPoint-----------------------------------
#include <Hazel/src/Hazel/entrypoint.h>
//-------------------------------------------------------

// #include <glm/vec3.hpp> // glm::vec3
// #include <glm/vec4.hpp> // glm::vec4
// #include <glm/mat4x4.hpp> // glm::mat4
// #include <glm/ext/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
// #include <glm/ext/matrix_clip_space.hpp> // glm::perspective
// #include <glm/ext/scalar_constants.hpp> // glm::pi

// glm::mat4 camera(float Translate, glm::vec2 const& Rotate)
// {
// 	glm::mat4 Projection = glm::perspective(glm::pi<float>() * 0.25f, 4.0f / 3.0f, 0.1f, 100.f);
// 	glm::mat4 View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -Translate));
// 	View = glm::rotate(View, Rotate.y, glm::vec3(-1.0f, 0.0f, 0.0f));
// 	View = glm::rotate(View, Rotate.x, glm::vec3(0.0f, 1.0f, 0.0f));
// 	glm::mat4 Model = glm::scale(glm::mat4(1.0f), glm::vec3(0.5f));
// 	return Projection * View * Model;
// }

// #include <glm/vec3.hpp> // glm::vec3
// #include <glm/vec4.hpp> // glm::vec4
// #include <glm/mat4x4.hpp> // glm::mat4
// #include <glm/ext/matrix_transform.hpp> // glm::translate, glm::rotate, glm::scale
// #include <glm/ext/matrix_clip_space.hpp> // glm::perspective
// #include <glm/ext/scalar_constants.hpp> // glm::pi

// glm::mat4 camera(float Translate, glm::vec2 const& Rotate)
// {
// 	glm::mat4 Projection = glm::perspective(glm::pi<float>() * 0.25f, 4.0f / 3.0f, 0.1f, 100.f);
// 	glm::mat4 View = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -Translate));
// 	View = glm::rotate(View, Rotate.y, glm::vec3(-1.0f, 0.0f, 0.0f));
// 	View = glm::rotate(View, Rotate.x, glm::vec3(0.0f, 1.0f, 0.0f));
// 	glm::mat4 Model = glm::scale(glm::mat4(1.0f), glm::vec3(0.5f));
// 	return Projection * View * Model;
// }

// int ReadImage(const std::string &filename, unsigned int *out_texture, int *out_width, int *out_height)
// {
//     cv::Mat image = cv::imread(filename, cv::IMREAD_COLOR);
//     if (image.empty())
//     {
//         std::cout << "no image data\n";
//         return 1;
//     }
//     if (image.channels() < 3)
//     {
//         cv::cvtColor(image, image, cv::COLOR_GRAY2BGR);
//     }
//     // cv::cvtColor(image,image,cv::COLOR_RGB2BGRA);

//     glGenTextures(1, out_texture);
//     glBindTexture(GL_TEXTURE_2D, *out_texture);

//     // Setup filtering parameters for display
//     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // This is required on WebGL for non
//     power-of-two textures glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Same

//     // glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
//     // glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//     if (image.channels() == 3)
//     {
//         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.size().width, image.size().height, 0, GL_RGB, GL_UNSIGNED_BYTE,
//         image.data);
//     }
//     else
//     {
//         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.size().width, image.size().height, 0, GL_RGBA,
//         GL_UNSIGNED_BYTE, image.data);
//     }
//     *out_width = image.size().width;
//     *out_height = image.size().height;
//     return 0;
// }

class ExampleLayer : public Hazel::Layer
{
  public:
    ExampleLayer() : Layer("example"), m_CameraController(1280.0f / 720.0f)
    {
        // auto cam=camera(5.0f,{0.5f,0.5f});
        m_VertexArray = Hazel::VertexArray::Create();

        float vertices[3 * 7] = {-0.5f, -0.5f, 0.0f, 0.8f, 0.2f, 0.3f, 1.0f, 0.5f, -0.5f, 0.0f, 0.2f,
                                 0.3f,  0.8f,  1.0f, 0.0f, 0.5f, 0.0f, 0.3f, 0.8f, 0.2f,  1.0f};
        Hazel::Ref<Hazel::VertexBuffer> vertexBuffer = Hazel::VertexBuffer::Create(vertices, sizeof(vertices));

        Hazel::BufferLayout layout = {{Hazel::ShaderDataType::Float3, "a_Position"},
                                      {Hazel::ShaderDataType::Float4, "a_Color"}};
        vertexBuffer->SetLayout(layout);
        m_VertexArray->AddVertexBuffer(vertexBuffer);

        uint32_t indices[3] = {0, 1, 2};

        Hazel::Ref<Hazel::IndexBuffer> indexBuffer =
            Hazel::IndexBuffer::Create(indices, sizeof(indices) / sizeof(uint32_t));
        m_VertexArray->SetIndexBuffer(indexBuffer);

        m_SquareVA = Hazel::VertexArray::Create();

        float squareVertices[5 * 4] = {-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.5f,  -0.5f, 0.0f, 1.0f, 0.0f,
                                       0.5f,  0.5f,  0.0f, 1.0f, 1.0f, -0.5f, 0.5f,  0.0f, 0.0f, 1.0f};

        Hazel::Ref<Hazel::VertexBuffer> squareVB = Hazel::VertexBuffer::Create(squareVertices, sizeof(squareVertices));

        squareVB->SetLayout({{Hazel::ShaderDataType::Float3, "a_Position"},

                             {Hazel::ShaderDataType::Float2, "a_TexCoord"}});
        m_SquareVA->AddVertexBuffer(squareVB);

        uint32_t squareIndices[6] = {0, 1, 2, 2, 3, 0};

        Hazel::Ref<Hazel::IndexBuffer> squareIB =
            Hazel::IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t));
        m_SquareVA->SetIndexBuffer(squareIB);

        std::string vertexSrc = R"(
            #version 330 core

            layout(location =0) in vec3 a_Position;
            layout(location =1) in vec4 a_Color;

            uniform mat4 u_ViewProjection;
            uniform mat4 u_Transform;

            out vec3 v_Position;
            out vec4 v_Color;
            void main()
            {
                v_Position=a_Position;
                v_Color=a_Color;
                gl_Position = u_ViewProjection * u_Transform * vec4(a_Position,1.0);
            }
        )";
        std::string fragmentSrc = R"(
            #version 330 core

            layout(location =0) out vec4 color;
            in vec3 v_Position;
            in vec4 v_Color;
            void main()
            {
                color=vec4(v_Position,1.0);
                color=v_Color;
            }
        )";

        m_Shader = Hazel::Shader::Create("vertexPosColor", vertexSrc, fragmentSrc);

        std::string vertexSrc2 = R"(
            #version 330 core

            layout(location =0) in vec3 a_Position;

            uniform mat4 u_ViewProjection;
            uniform mat4 u_Transform;

            out vec3 v_Position;

            void main()
            {
                v_Position=a_Position;
                gl_Position = u_ViewProjection*u_Transform*vec4(a_Position,1.0);
            }
        )";
        std::string fragmentSrc2 = R"(
            #version 330 core

            layout(location =0) out vec4 color;
            in vec3 v_Position;
            
            uniform vec3 u_Color;

            void main()
            {
                color=vec4(u_Color,1.0);
            }
        )";
        m_Shader2 = Hazel::Shader::Create("flatColor", vertexSrc2, fragmentSrc2);

        std::string TestureVertexSrc = R"(
            #version 330 core

            layout(location =0) in vec3 a_Position;
            layout(location =1) in vec2 a_TexCoord;

            uniform mat4 u_ViewProjection;
            uniform mat4 u_Transform;

            out vec3 v_Position;
            out vec2 v_TexCoord;

            void main()
            {
                v_TexCoord=a_TexCoord;
                gl_Position = u_ViewProjection*u_Transform*vec4(a_Position,1.0);
            }
        )";
        std::string TextureFragmentSrc = R"(
            #version 330 core

            layout(location =0) out vec4 color;
    
            in vec2 v_TexCoord;
            
            uniform sampler2D u_Texture;

            void main()
            {
                color=texture(u_Texture,v_TexCoord);
            }
        )";
        // m_TextureShader.reset(Hazel::Shader::Create(TestureVertexSrc,TextureFragmentSrc));
        // m_TextureShader = Hazel::Shader::Create("G:\\test\\hazel\\assert\\shaders\\Texture.glsl");
        auto m_textureShader = m_ShaderLibrary.Load("..\\..\\assert\\shaders\\Texture.glsl");
        // if(!ReadImage("G:\\work\\test\\bin\\Release\\Images.bmp",&m_out_texture,&Imagewidth,&Imageheight))
        // {
        //     std::cout<<"width:="<<Imagewidth<<",height:="<<Imageheight<<'\n';
        //     // ImGui::Image((ImTextureID)out_texture,ImVec2(width, height));
        // }

        m_Texture = Hazel::Texture2D::Create("..\\..\\assert\\texture2d\\Image.png");
        std::dynamic_pointer_cast<Hazel::OpenGLShader>(m_textureShader)->Bind();
        std::dynamic_pointer_cast<Hazel::OpenGLShader>(m_textureShader)->UploadUniformInt("u_Texture", 0);
    }

    void OnUpdate(Hazel::Timestep ts) override
    {
        // update
        m_CameraController.OnUpdate(ts);

        // render
        Hazel::RendererCommand::SetClearColor({0.1f, 0.1f, 0.1f, 1});
        Hazel::RendererCommand::Clear();

        Hazel::Renderer::BeginScene(m_CameraController.GetCamera());

        glm::mat4 scale = glm::scale(glm::mat4(1.0f), glm::vec3(0.1f));

        std::dynamic_pointer_cast<Hazel::OpenGLShader>(m_Shader2)->Bind();
        std::dynamic_pointer_cast<Hazel::OpenGLShader>(m_Shader2)->UploadUniformFloat3("u_Color", m_SquareColor);

        for (int y = 0; y < 20; y++)
        {
            for (int x = 0; x < 20; x++)
            {
                glm::vec3 pos(x * 0.11, y * 0.11, 0.0f);
                glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos) * scale;

                // if(x%2==0)
                // {
                //     m_Shader2->UploadUniformFloat4("u_Color",redColor);
                // }else
                // {
                //     m_Shader2->UploadUniformFloat4("u_Color",blueColor);
                // }
                Hazel::Renderer::Submit(m_SquareVA, m_Shader2, transform);
            }
        }

        auto textureShader = m_ShaderLibrary.Get("Texture");
        m_Texture->Bind();
        Hazel::Renderer::Submit(m_SquareVA, textureShader, glm::scale(glm::mat4(1.0f), glm::vec3(1.5f)));
        // Hazel::Renderer::Submit(m_VertexArray,m_Shader);
        Hazel::Renderer::EndScene();
    }

    virtual void OnImGuiRender() override
    {
        ImGui::Begin("settings");
        ImGui::ColorEdit3("Square Color", glm::value_ptr(m_SquareColor));

        ImGui::End();

        ImGui::Begin("Image");
        ImGui::Image((ImTextureID)m_out_texture, ImVec2(Imagewidth / 2, Imageheight / 2));
        ImGui::End();
    }

    void OnEvent(Hazel::Event &e) override
    {
        m_CameraController.OnEvent(e);
    }

  private:
    Hazel::ShaderLibrary m_ShaderLibrary;
    Hazel::Ref<Hazel::Shader> m_Shader;
    Hazel::Ref<Hazel::Shader> m_Shader2, m_TextureShader;
    Hazel::Ref<Hazel::VertexArray> m_VertexArray;
    Hazel::Ref<Hazel::VertexArray> m_SquareVA;

    Hazel::Ref<Hazel::Texture2D> m_Texture;

    Hazel::OrthographicCameraController m_CameraController;

    glm::vec3 m_SquarePosition;

    unsigned int m_out_texture = 0;
    int Imagewidth = 0, Imageheight = 0;

    std::string m_image_file;
    glm::vec3 m_SquareColor = {0.2f, 0.3f, 0.8f};
};

class SandBox : public Hazel::Application
{
  public:
    SandBox():Hazel::Application("sand box")
    {
        // PushLayer(new ExampleLayer());
        PushLayer(new Sandbox2D());
    }
    ~SandBox(){};
};

Hazel::Application *Hazel::create_application()
{
    return new SandBox();
}